package com.googlecode.bip.servlet.filter;

import cx.ath.choisnet.lang.ByteArrayBuilder;

/**
 *
 * @author Claude CHOISNET
 */
public class ByteArrayBuilderReplace
{
    private ByteArrayBuilder bab;

    public ByteArrayBuilderReplace( ByteArrayBuilder bab )
    {
        this.bab = bab;
    }

    public ByteArrayBuilder getByteArrayBuilder()
    {
        return bab;
    }

    /**
     * @return true if internal ByteArrayBuilder have been modify.
     */
    public boolean replaceAll(
        final byte[]    pattern,
        final byte[]    replace
        )
    {
        byte[] sbytes = bab.array();
        byte[] dbytes = replaceAll( sbytes , pattern, replace );

        if( sbytes == dbytes ) {
            // Same array, no change
            return false;
            }
        else {
            // Set new ByteArrayBuilder
            bab = new ByteArrayBuilder( dbytes );

            return true;
            }
    }

    /**
     *
     */
    private static byte[] replaceAll(
        final byte[]    sbytes,
        final byte[]    pattern,
        final byte[]    replace
        )
    {
        byte[] dbytes;

        int i = KPM.indexOf( sbytes, pattern );

        if( i>=0 ) {
            ByteArrayBuilder babDest = new ByteArrayBuilder();
            int              offset  = 0;

            do {
                if( i > 0 ) {
                    // add before pattern
                    babDest.append( sbytes, offset, i - offset);
                    }
                // add replace
                babDest.append( replace );

                offset = i + pattern.length;

                i = KPM.indexOf( sbytes, offset, pattern );
                } while( i >= 0 );
            // add after last pattern
            babDest.append( sbytes, offset, sbytes.length - offset );

            dbytes = babDest.array();
            }
        else {
            dbytes = sbytes;
            }

        return dbytes;
    }

    /**
     * The Knuth-Morris-Pratt Pattern Matching Algorithm can be used
     * to search a byte array.
     */
    private static class KPM
    {
        /**
         * Search the data byte array for the first occurrence
         * of the byte array pattern.
         */
        public static int indexOf(byte[] data, byte[] pattern)
        {
            return indexOf( data, 0, pattern );
        }

        /**
         * Search the data byte array for the first occurrence
         * of the byte array pattern.
         */
        public static int indexOf(byte[] data, int from, byte[] pattern)
        {
            int[] failure = computeFailure(pattern);

            int j = 0;

            for (int i = from/*0*/; i < data.length; i++) {
                while (j > 0 && pattern[j] != data[i]) {
                    j = failure[j - 1];
                }
                if (pattern[j] == data[i]) {
                    j++;
                }
                if (j == pattern.length) {
                    return i - pattern.length + 1;
                }
            }
            return -1;
        }

        /**
         * Computes the failure function using a boot-strapping process,
         * where the pattern is matched against itself.
         */
        private static int[] computeFailure( byte[] pattern )
        {
            int[] failure = new int[pattern.length];

            int j = 0;
            for (int i = 1; i < pattern.length; i++) {
                while (j>0 && pattern[j] != pattern[i]) {
                    j = failure[j - 1];
                }
                if (pattern[j] == pattern[i]) {
                    j++;
                }
                failure[i] = j;
            }

            return failure;
        }
    }
}

